package org.amos.core.frame.aspect;



import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.amos.core.basic.annotation.CacheBatchEvict;
import org.amos.core.frame.utils.RedisUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @desc: redis 缓存注解切面
 * @author: liubt
 * @date: 2022-09-11 17:32
 **/
@Slf4j
@Aspect
@Component
public class CacheAspect {
    @Autowired
    private RedisUtils redisUtils;

    @Pointcut("@annotation(org.amos.core.basic.annotation.CacheBatchEvict)")
    public void pointcut() {

    }

    @Around("pointcut()")
    public Object proceed(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 1.获取注解参数值 SpEL表达式
        CacheBatchEvict cacheBatchEvict = methodSignature.getMethod().getAnnotation(CacheBatchEvict.class);
        String[] cacheNames = cacheBatchEvict.cacheNames();

        String spEL = cacheBatchEvict.key();
        if (StrUtil.isBlank(spEL)) {
            log.warn("@CacheBatchEvict key is null");
            return null;
        }
        // 2.获取目标方法参数
        List<Object> keyList = generateKeyListBySpEL(spEL, joinPoint);
        if (CollUtil.isEmpty(keyList)) {
            log.warn("unable to find method params by [{}]", spEL);
            return null;
        }
        // 3.清除缓存
        for (String cacheName : cacheNames) {
            List<String> cacheKeyList = keyList.stream().map(x -> cacheName.concat(":").concat(String.valueOf(x))).collect(Collectors.toList());
            redisUtils.del(cacheKeyList.toArray(cacheKeyList.toArray(new String[]{})));
        }
        // 4.执行目标方法
        return joinPoint.proceed();
    }

    public List<Object> generateKeyListBySpEL(String spEL, ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        // 创建解析器
        SpelExpressionParser parser = new SpelExpressionParser();
        // 获取表达式
        Expression expression = parser.parseExpression(spEL);
        // 设置解析上下文
        EvaluationContext context = new StandardEvaluationContext();
        Object[] args = joinPoint.getArgs();
        // 获取运行时参数名称
        DefaultParameterNameDiscoverer discover = new DefaultParameterNameDiscoverer();
        String[] parameterNames = discover.getParameterNames(method);
        assert parameterNames != null;
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        // 解析
        List<Object> valueList = (List<Object>) expression.getValue(context);
        if (CollUtil.isEmpty(valueList)) {
            return new ArrayList<>();
        }
        return valueList;
    }
}

